using System;
using System.Collections.ObjectModel;
using System.ComponentModel.Composition;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
using Crm2011.Framework.Core;
using Microsoft.Practices.Prism.Events;
using Microsoft.Practices.Prism.ViewModel;
using ScriptEase.Shell.Events;



namespace ScriptEase.Shell
{
    /// <summary>
    /// The ShellViewModel is used as a dispatcher 
    /// between the business logic and the ShellView.
    /// </summary>
    [Export(typeof (ShellViewModel))]
    public class ShellViewModel : NotificationObject
    {
        private ProjectStatus _projectLoadStatus;
        private Solution _selectedSolution;
        private ShellView _view;



        /// <summary>
        /// Initializes the model after the specified view was initialized.
        /// </summary>
        public void Initialize(ShellView view)
        {
            _view = view;
            this.ProjectLoadStatus = ProjectStatus.Closed;
            this.EventAggregator.GetEvent<ConnectionStringChangedEvent>().Subscribe(connectionStringChanged, ThreadOption.BackgroundThread);

            string mostRecentProjectName = MruList.Current.MostRecent;
            if (mostRecentProjectName != null) this.OpenProject(mostRecentProjectName);
        }



        [Import]
        public IEventAggregator EventAggregator { get; set; }


        public Project Project { get; private set; }


        internal ProjectStatus ProjectLoadStatus
        {
            get { return _projectLoadStatus; }
            private set
            {
                if (_projectLoadStatus == value) return;
                _projectLoadStatus = value;
                base.RaisePropertyChanged(() => this.ProjectLoadStatus);
            }
        }


        public Solution SelectedSolution
        {
            get { return _selectedSolution; }
            set
            {
                if (_selectedSolution == value) return;
                _selectedSolution = value;
                base.RaisePropertyChanged(() => this.SelectedSolution);

                var userData = UserDataList.Current.Get(this.Project.Data.ConnectionId);
                userData.CurrentSolutionUniqueName = _selectedSolution.UniqueName;
                UserDataList.Current.Set(userData);

                this.Project.FilterBySolution(_selectedSolution.UniqueName);
            }
        }

        public readonly ObservableCollection<Solution> CrmSolutions = new ObservableCollection<Solution>();



        /// <summary>
        /// Opens a project.
        /// </summary>
        /// <param name="projectFileName">Name of the project file.</param>
        /// <remarks>
        /// Set-up a new project, initialize the UI with the porject's information 
        /// and set the ProjectLoadStatus
        /// </remarks>
        public void OpenProject(string projectFileName)
        {
            try
            {
                if (this.Project != null) throw new ApplicationException("You must close the project first!");

                ProjectConfiguration projectConfiguration = Project.LoadFromFile(projectFileName);
                this.Project = new Project(projectConfiguration);

                MruList.Current.Add(this.Project.Data.FilePath);
                this.ProjectLoadStatus = ProjectStatus.LoadedDisconnected;
            }
            catch (Exception e)
            {
                // When open fails the previous project status remains unchanged!
                Trace.Default.LogException(e);
            }
        }



        public void Disconnect()
        {
            if (this.Project == null) throw new ApplicationException("You cannot disconnect from a project that is not loaded!");
            this.Project.Disconnect();
            this.ProjectLoadStatus = ProjectStatus.LoadedDisconnected;
        }



        public void CloseProject()
        {
            if (this.Project != null) this.Disconnect();
            this.Project = null;
            this.ProjectLoadStatus = ProjectStatus.Closed;
        }



        public void CloseApp()
        {
            this.CloseProject();
            _view.Close();
        }



        public string GetCaption()
        {
#if DEBUG
            const string build = "DEBUG";
#else
            const string build = "RELEASE";
#endif
#if BETA
            const string release = "[BETA]";
#else
            const string release = "";
#endif

            return string.Format("ScriptEase for Microsoft Dynamics CRM - v{0} ({1} build {2})", Assembly.GetExecutingAssembly().GetName().Version, build, release);
        }



        /// <summary>
        /// Called on a background thread when the 
        /// connection to CRM has changed!
        /// </summary>
        /// <param name="args">The <see cref="ConnectionStringChangedEventArgs"/> instance 
        /// containing the event data.</param>
        private void connectionStringChanged(ConnectionStringChangedEventArgs args)
        {
            try
            {
                if (!this.Project.IsKnownConnection(args.ConnectionString))
                {
                    string message = string.Format("The project\r\n"
                        + "  {1}\r\n\r\n"
                        + "has never been connected with\r\n"
                        + "{0}\r\n\r\n"
                        +"After connecting with this CRM-Organization, all Scripts will be downloaded and stored in the project's directory. "
                        +"If the organization's Scripts are not 'comaptible' with the local Scripts of the current project, "
                        +"the project's Script directory will become a mixture of two different organizations."
                        , args.ConnectionString.OrganizationUrl, this.Project.ProjectName);

                    const string question = "Are you sure you want to connect with that CRM-System?";

                    var result = MessageBox.Show(message + Environment.NewLine+Environment.NewLine + question, "New connection for a project",
                                                 MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button3);
                    
                    if (result != DialogResult.Yes)
                        throw new ApplicationException("Conenction string changed user-abort!");
                }

                this.Project.SetKnownConnection(args.ConnectionString);

                this.ProjectLoadStatus = ProjectStatus.Connecting;
                this.Project.Connect(args.ConnectionString);
                conenctionSucceeded(this.Project, args.ConnectionString, args.ConnectionListId);
            }
            catch (Exception e)
            {
                connectionFailed(e);
            }
        }



        /// <summary>
        /// Connection with CRM successfully established - UI safe.
        /// </summary>
        /// <param name="connectedProject">The connected project.</param>
        /// <param name="connectionString">The connection string.</param>
        /// <param name="connectionListId">The connection list id.</param>
        private void conenctionSucceeded(Project connectedProject, CrmConnectionString connectionString, Guid connectionListId)
        {
            #region Thread-safe invoke


            if (_view.InvokeRequired)
            {
                _view.Invoke(new MethodInvoker(() => conenctionSucceeded(connectedProject, connectionString, connectionListId)));
                return;
            }


            #endregion


            this.ProjectLoadStatus = ProjectStatus.Connected;
            this.Project.Data.ConnectionId = connectionListId;
            Project.SaveToFile(connectedProject.Data);

            this.CrmSolutions.Clear();
            foreach (Solution solution in connectedProject.Solutions)
            {
                this.CrmSolutions.Add(solution);
            }

            //
            // Try to get the user's conenction related data
            //
            var userData = UserDataList.Current.Get(connectedProject.Data.ConnectionId);

            Solution mostRecentSolution = null;
            if (userData.CurrentSolutionUniqueName != null)
            {
                // try to find the last solution the user had opened
                mostRecentSolution = connectedProject.Solutions.FirstOrDefault(so => 0 == String.Compare(so.UniqueName, userData.CurrentSolutionUniqueName, StringComparison.InvariantCultureIgnoreCase));
            }

            if (mostRecentSolution == null)
            {
                // get default solution
                mostRecentSolution = connectedProject.Solutions.FirstOrDefault(so => 0 == String.Compare(so.UniqueName, "Default", StringComparison.InvariantCultureIgnoreCase));
            }
            this.SelectedSolution = mostRecentSolution;
        }



        /// <summary>
        /// Connecting to CRM failed - UI safe!
        /// </summary>
        private void connectionFailed(Exception error)
        {
            #region Thread-safe invoke


            if (_view.InvokeRequired)
            {
                _view.Invoke(new MethodInvoker(() => connectionFailed(error)));
                return;
            }


            #endregion

            this.ProjectLoadStatus = ProjectStatus.LoadedDisconnected;
            Trace.Default.LogException(error);
        }     
    }
}